/*
   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License version 2 as published by the Free Software Foundation.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301, USA.
*/

#include "ifunction.h"
template<> double Math::IFunction<double>::Parser::evaluate(const double& _x) const {
    m_fVal = _x ; 
    double ret =  m_pParser->Eval();
    return ret ; 
}
template<> double Math::IFunction<double>::value(const double & _ran, int pos ) const { 
    try{
        if ( m_FunctionType == Math::eContinuous ) {
            return m_Parser.evaluate(_ran);
        }
        else if ( m_FunctionType == Math::eDiscrete ) {
            return m_pValues[pos];
        }
    }
    catch ( std::domain_error & e ) {
        throw Math::FunctionException(e.what() ) ; 
    }
    catch( mu::Parser::exception_type & e ) {
        throw Math::FunctionException(e.GetMsg() ) ; 
    }
	throw Math::FunctionException( "Math::IFunction<double>::value() shouldn't be here " ) ; 
}
template<> void Math::IFunction<double>::recalculateData ( int _size, const double & _rangeMin, const double & _rangeMax, double _step ) {
    if ( m_FunctionType == Math::eDiscrete ) 
        return ; 
    
    if ( _size > m_Size ) {
        m_Size = _size ;
        m_pRange.reset( new double[m_Size] );
        m_pValues.reset( new double[m_Size] );
    }
    else {
        ;
    }
    double iter = _rangeMin ; 
    for ( int i = 0 ; i < _size ; ++i ) 
    {
        if ( iter > _rangeMax ) 
            break ; 
        try{ 
            m_pValues[i] = value(iter); // this will throw exception
            m_pRange[i] = iter ; 
        }
        catch ( Math::FunctionException & exception) {
            qDebug() << " This will not be evaluated" << exception.what();
            i-- ; 
        }
        iter += _step;
    }
    m_NeedToRecalculate = false ; 
}
template<> Math::IFunction<double> * Math::IFunction<double>::calculateDerivative( const double & _startX, const double & _stopX, double _step, DerivativeMethod _dMethod /*= Math::eFiniteDifferenceTwoPoints */) const {
    ///FIXME: value throws exceptions!
    int numberOfPoints = (abs(_startX ) + abs( _stopX ) )/_step ; 
    Math::IFunction<double> * pFunction = new Math::IFunction<double>() ; 
    double *pX;
    double *pY;
    int howManyPoints = (int) ( (abs(_startX) + abs(_stopX))/_step );
    int boundary = m_FunctionType==Math::eDiscrete ? howManyPoints - 1 : howManyPoints ;
    pX = new double [ boundary ];
    pY = new double [ boundary ];
    double iterator = _startX ;  
    double fx ;
    double fxWithH ; 
//     int boundary = m_FunctionType==Math::eDiscrete ? howManyPoints - 1 : howManyPoints ;
    int realSize = 0; 
    for (int i = 0 ; i < boundary; ++i) {
        if (iterator >= _stopX ) {
            break ; 
        }
        try{
            fx = value( iterator , i );
            fxWithH = value( iterator + _step, i + 1 ) ;
            pX[i] = iterator ; 
            pY[i] = (fxWithH - fx)/_step ; 
            iterator += _step ;
            ++realSize ; 
        }

        catch ( Math::FunctionException & e ) {
            qDebug() << e.what() ; 
            --i ; 
        }
    }
    pFunction->setData( pX, pY , realSize );
    return pFunction ; 
}

//     template<> double IFunction<Point>::value(const Point& _ran) const {
//        return _ran.x() * _ran.y() ; 
//     }
template<> Math::IFunction<double>::Parser::Parser(const QString& _equation, const QStringList& listOfVars) {
    m_pParser.reset( Math::ParserFactory::factory() );
    Q_CHECK_PTR(m_pParser.get());
    if ( !_equation.isEmpty() ) {
        m_pParser->SetExpr(_equation.toStdString() );
    }
    for ( int i=0 ; i< listOfVars.size() ; ++i ) {
        m_pParser->DefineVar( listOfVars.at(i).toStdString(), &m_fVal );
    }
}
template<> void Math::IFunction<double>::Parser::setVars( const QStringList & _vars ) {
    m_pParser->DefineVar(_vars.at(0).toStdString(),&m_fVal);
}
